using Assets.Scripts;
using Assets.Scripts.Atmospherics;
using Assets.Scripts.GridSystem;
using Assets.Scripts.Networking;
using Assets.Scripts.Networks;
using Assets.Scripts.Objects;
using Assets.Scripts.Objects.Clothing;
using Assets.Scripts.Objects.Electrical;
using Assets.Scripts.Objects.Entities;
using Assets.Scripts.Objects.Items;
using Assets.Scripts.Objects.Motherboards;
using Assets.Scripts.Objects.Pipes;
using Assets.Scripts.Util;
using Networks;
using System;
using System.Linq;
using UnityEngine.Diagnostics;
using Util.Commands;
using VisualmodBep;

namespace StationeersServerPreference;

public class SspAtmosphere {
    private static SspAtmosphere _instance;
    public static SspAtmosphere Instance { get => _instance ??= new SspAtmosphere(); }
    protected static Func<string, string> T = Translation.T;

    public Atmosphere atmosphere { get; private set; }
    public Human parentHuman;
    public AtmosphereText text { get; private set; }
    public IThermal thermal { get; private set; }

    public class GasText {
        public readonly string name; // gas and liquid

        public readonly Chemistry.GasType gasType;
        public string gasMoles; // gas quantity, w/o unit (mol)
        public string gasPercent; // gas percentage, with %

        public readonly Chemistry.GasType liquidType;
        public string liquidMoles; // liquid quantity, w/o unit (mol)
        public string liquidPercent; // liquid percentage, with %
        public string liquidVolume; // liquid volume, w/o unit (L)

        public bool freezing;

        public static readonly Mole EmptyMole = Mole.Create(Chemistry.GasType.PollutedWater);

        public GasText(string name, Chemistry.GasType gasType, Chemistry.GasType liquidType) {
            this.name = name;
            this.gasType = gasType;
            this.liquidType = liquidType;
        }

        public void StatGas(Atmosphere atmos, Mole mole, Mole moleLq) {
            try {
                Clear();
                float percentage = 0f;
                float qTotal = atmos.GasMixture.GetTotalMolesGassesAndLiquids.ToFloat();
                float q = mole.Quantity.ToFloat();
                if (q > 0f) {
                    gasMoles = q.RoundToSignificantDigits(3).ToStringPrefix();
                    // gas volume is shared
                    percentage = q / qTotal * 100f;
                    gasPercent = $"{percentage:N3}%";
                    TemperatureKelvin closeToFreeze = mole.FreezingTemperature() + TemperatureKelvin.One;
                    freezing = q > 1E-05f && atmos.Temperature <= closeToFreeze;
                    /*if (mole.CheckChangeState(atmosphere.PressureGassesAndLiquids) == GasItem.StateSymbolType.condensation)
                    {
                        stateSymbolType |= GasItem.StateSymbolType.condensation;
                    }
                    if (mole.CheckChangeState(atmosphere.PressureGassesAndLiquids) == GasItem.StateSymbolType.evaporation)
                    {
                        stateSymbolType |= GasItem.StateSymbolType.evaporation;
                    }*/
                }
                float qLq = moleLq.Quantity.ToFloat();
                if (qLq > 0f) {
                    liquidMoles = qLq.RoundToSignificantDigits(3).ToStringPrefix();
                    float vLq = moleLq.Volume.ToFloat();
                    liquidVolume = vLq.RoundToSignificantDigits(3).ToStringPrefix();
                    percentage = q / qTotal * 100f;
                    liquidPercent = $"{percentage:N3}%";
                }
            } catch (Exception) {
            }
        }

        public void Clear() {
            gasPercent = gasMoles = "";
            liquidPercent = liquidMoles = liquidVolume = "";
        }
    }

    public class AtmosphereText {
        public string name;
        public string capacity; // with unit (L)
        public string converted; // with unit (J)
        public string latent; // with unit (J)
        public string liquid; // with unit (L)
        public string pressure; // with unit (Pa)
        public string radiated; // with unit (J)
        public string stress; // gas pipe stress, with %
        public string temperature; // with unit (K)
        public GasText[] mix;

        // 3 CarbonDioxide means `Chemistry.GasType.CarbonDioxide == 1 << 3`;
        // GasType.Air = Oxygen | Nitrogen;
        // GasType.Fuel = Oxygen | Volatiles;
        public AtmosphereText() {
            mix = new GasText[9] {
// [0] 1 Oxygen, 9 LiquidOxygen
new GasText("O2", Chemistry.GasType.Oxygen, Chemistry.GasType.LiquidOxygen),
// [1] 2 Nitrogen, 8 LiquidNitrogen
new GasText("N2", Chemistry.GasType.Nitrogen, Chemistry.GasType.LiquidNitrogen),
// [2] 3 CarbonDioxide, 12 LiquidCarbonDioxide
new GasText("CO2", Chemistry.GasType.CarbonDioxide, Chemistry.GasType.LiquidCarbonDioxide),
// [3] 4 Volatiles, 10 LiquidVolatiles
new GasText("VOL", Chemistry.GasType.Volatiles, Chemistry.GasType.LiquidVolatiles),
// [4] 5 Pollutant, 13 LiquidPollutant
new GasText("X", Chemistry.GasType.Pollutant,Chemistry.GasType.LiquidPollutant),
// [5] 11 Steam, 6 Water
new GasText("H2O", Chemistry.GasType.Steam, Chemistry.GasType.Water),
// [6] 7 NitrousOxide, 14 LiquidNitrousOxide
new GasText("N2O",Chemistry.GasType.NitrousOxide, Chemistry.GasType.LiquidNitrousOxide),
// [7] 15 Hydrogen, 16 LiquidHydrogen
new GasText("H2", Chemistry.GasType.Hydrogen, Chemistry.GasType.LiquidHydrogen),
// [8] 17 PollutedWater
new GasText("H2O(P)", Chemistry.GasType.Undefined, Chemistry.GasType.PollutedWater)
            };
        }

        // don't clear name
        public void Clear() {
            capacity = liquid = pressure = temperature = "";
            converted = latent = radiated = stress = "";
            foreach (GasText gas in mix) {
                gas.Clear();
            }
        }

        public void UpdateMixture(Atmosphere atmosphere) {
            mix[0].StatGas(atmosphere, atmosphere.GasMixture.Oxygen, atmosphere.GasMixture.LiquidOxygen);
            mix[1].StatGas(atmosphere, atmosphere.GasMixture.Nitrogen, atmosphere.GasMixture.LiquidNitrogen);
            mix[2].StatGas(atmosphere, atmosphere.GasMixture.CarbonDioxide, atmosphere.GasMixture.LiquidCarbonDioxide);
            mix[3].StatGas(atmosphere, atmosphere.GasMixture.Volatiles, atmosphere.GasMixture.LiquidVolatiles);
            mix[4].StatGas(atmosphere, atmosphere.GasMixture.Pollutant, atmosphere.GasMixture.LiquidPollutant);
            mix[5].StatGas(atmosphere, atmosphere.GasMixture.Steam, atmosphere.GasMixture.Water);
            mix[6].StatGas(atmosphere, atmosphere.GasMixture.NitrousOxide, atmosphere.GasMixture.LiquidNitrousOxide);
            mix[7].StatGas(atmosphere, atmosphere.GasMixture.Hydrogen, atmosphere.GasMixture.LiquidHydrogen);
            mix[8].StatGas(atmosphere, GasText.EmptyMole, atmosphere.GasMixture.PollutedWater);
        }
    };

    public SspAtmosphere() {
        atmosphere = null;
        parentHuman = null;
        text = new AtmosphereText();
        thermal = null;
        text.Clear();
    }

    /// @ref AtmosAnalyser.PrepareText
    public static bool OnUpdate() {
        // @note use Instance to check if _instance created
        if (Instance.parentHuman == null) {
            Instance.parentHuman = Util.GetHuman(true);
        }
        bool changed = UpdateActualAtmosphere();
        _instance.UpdateText(changed);
        return changed;
    }

    // @ref AtmosAnalyser.GetScannedAtmosphere()
    public static Atmosphere GetCursorAtmosphere(Human rootParentHuman = null) {
        if (rootParentHuman == null) {
            return null; // maybe not in game
        }
        Thing cursorThing = CursorManager.CursorThing;
        if (cursorThing != null) {
            if (cursorThing.InternalAtmosphere != null) {
                // Log.LogInfo($"GetCursorAtmosphere InternalAtmosphere");
                return cursorThing.InternalAtmosphere;
            }
            INetworkedAtmospherics networkedAtmospherics = cursorThing as INetworkedAtmospherics;
            AtmosphericsNetwork atmosphericsNetwork = ((networkedAtmospherics != null) ? networkedAtmospherics.StructureNetwork : null) as AtmosphericsNetwork;
            if (atmosphericsNetwork != null) {
                return atmosphericsNetwork.Atmosphere;
            }
            GasTankStorage gasTankStorage = cursorThing as GasTankStorage;
            if (gasTankStorage && gasTankStorage.Slots[0].IsNotEmpty()) {
                GasCanister gasCanister = gasTankStorage.Slots[0].Get() as GasCanister;
                return gasCanister.InternalAtmosphere;
            }
            Human human = cursorThing as Human; // point to human
            if (human) {
                ISuit suit = human.Suit;
                if (((suit != null) ? suit.AsThing : null) && human.Suit.AsThing.HasReadableAtmosphere) {
                    return human.Suit.InternalAtmosphere;
                }
            }
            VendingMachineRefrigerated vendingMachineRefrigerated = cursorThing as VendingMachineRefrigerated;
            if (vendingMachineRefrigerated && vendingMachineRefrigerated.InternalAtmosphere != null) {
                return vendingMachineRefrigerated.InternalAtmosphere;
            }
        }
        // no cursor thing
        if (rootParentHuman.WorldAtmosphere != null) {
            // Log.LogInfo($"GetCursorAtmosphere World");
            return rootParentHuman.WorldAtmosphere;
        } else {
            Log.LogError($"GetCursorAtmosphere World == null");
            return null;
        }
    }

    private static bool UpdateActualAtmosphere() {
        Atmosphere atmosphere = GetCursorAtmosphere(_instance.parentHuman);
        IThermal thermal = CursorManager.CursorThing as IThermal; // addtional check?
        if (thermal != null) {
            _instance.thermal = thermal;
            if (thermal is PipeRadiator) {
                atmosphere = null;
            } else if (atmosphere == null || atmosphere.Mode == AtmosphereHelper.AtmosphereMode.World) {
                atmosphere = thermal.ThermalAtmosphere;
            }
        }
        if (atmosphere != _instance.atmosphere) {
            _instance.atmosphere = atmosphere;
            return true;
        }
        return false;
    }

    /// @ref Assets.Scripts.Objects.Items.AtmosAnalyser.PrepareText()
    public void UpdateText(bool changed) {
        try {
            if (changed) {
                text.name = GetAtmosphereName(atmosphere);
                if (atmosphere != null) {
                    text.name += $" #{atmosphere.ReferenceId}"; // optional
                }
            }
            if (atmosphere == null) {
                text.Clear();
                return;
            }
            // status
            float pascal = atmosphere.PressureGassesAndLiquidsInPa;
            if (pascal > 1E-42f) {
                text.pressure = pascal.RoundToSignificantDigits(3).ToStringPrefix(Chemistry.PascalUnit);
                text.temperature = $"{atmosphere.Temperature:N2} K";
            } else {
                text.pressure = T("Atmosphere.Vacuum");
                text.temperature = "N/A";
            }
            float vAtmo = atmosphere.Volume.ToFloat();
            float vLq = atmosphere.TotalVolumeLiquids.ToFloat();
            text.capacity = $"{vAtmo.RoundToSignificantDigits(1):F1} L";
            if (atmosphere.TotalVolumeLiquids > VolumeLitres.Zero) {
                text.liquid = $"{vLq.RoundToSignificantDigits(3):F3} L";
            } else {
                text.liquid = "";
            }

            bool isGasPipe = atmosphere.Mode == AtmosphereHelper.AtmosphereMode.Network && atmosphere.AllowedMatterState == AtmosphereHelper.MatterState.Gas;
            if (isGasPipe) {
                float stress = vLq / vAtmo * 5000f;
                text.stress = $"{stress:N2}%";
            } else {
                text.stress = "";
            }
            if (thermal != null) {
                text.converted = AtmosAnalyser.GetEnergyUnitString(thermal.EnergyConvected);
                text.radiated = AtmosAnalyser.GetEnergyUnitString(thermal.EnergyRadiated);
                text.latent = AtmosAnalyser.GetEnergyUnitString(atmosphere.LastTickLatentEnergy.ToFloat());
            } else {
                text.converted = text.radiated = text.latent = "";
            }
            text.UpdateMixture(atmosphere);
        } catch (Exception ex) {
            Log.LogError($"SspAtmosphere UpdateText {ex}");
        }
    }

    public static string GetAtmosphereName(Atmosphere atmosphere) {
        if (atmosphere == null) {
            return "-";
        }
        string name = "error";
        switch (atmosphere.Mode) {
        case AtmosphereHelper.AtmosphereMode.World:
        case AtmosphereHelper.AtmosphereMode.Global:
            Grid3 grid = atmosphere.Grid;
            Room room = GridController.World.RoomController.GetRoom(grid);
            name = room == null ? T("Atmosphere.World") : $"{T("Atmosphere.Room")} {room.RoomId}";
            name += $" ({grid.x},{grid.z},{grid.y})"; // xzy to xyz
            break;
        case AtmosphereHelper.AtmosphereMode.Network:
            string network = atmosphere.AtmosphericsNetwork is PipeNetwork ? T("Atmosphere.Pipe") : "LandingPad";
            name = $"{network} {atmosphere.AtmosphericsNetwork.ReferenceId}";
            break;
        case AtmosphereHelper.AtmosphereMode.Thing:
            name = atmosphere.Thing != null ? atmosphere.Thing.DisplayName : "Thing";
            break;
        }
        // Log.LogInfo($"AtomsphereName \"{atmosphere.DisplayName}\"@{atmosphere.Index} \"{ret}\"");
        return name;
    }

    // @ref AddGasCommand.Process()
    public static Atmosphere GetAtmosphereByRefId(long refId) {
        IReferencable r = Referencable.Find(refId);
        if (r == null) {
            return null;
        }
        PipeNetwork pipeNetwork = r as PipeNetwork;
        if (pipeNetwork != null) {
            return pipeNetwork.Atmosphere;
        }
        Thing thing = r as Thing;
        if (thing != null) {
            if (thing.InternalAtmosphere != null) {
                return thing.InternalAtmosphere;
            }
            Pipe pipe = thing as Pipe;
            if (pipe != null) {
                return pipe.PipeNetwork.Atmosphere;
            }
        }
        return r as Atmosphere;
    }

    // @ref Human.CreateLungs()
    // give quantity and temperature, use atmosphere's volume
    public static void AddGas(Atmosphere atmosphere, Chemistry.GasType gasType, double moles, double kelvin) {
        if (atmosphere != null && moles > 0.0) {
            MoleQuantity q = new MoleQuantity(moles);
            TemperatureKelvin t = new TemperatureKelvin(kelvin);
            MoleEnergy e = new MoleEnergy(t, Mole.GetSpecificHeat(gasType), q);
            Mole mole = new Mole(gasType, q, e);
            atmosphere.Add(mole);
        }
    }

    // give pressure, volume and temperature
    public static void AddGasPVT(Atmosphere atmosphere, Chemistry.GasType gasType, double kpa, double liters, double kelvin) {
        if (atmosphere != null && liters > 0.0) {
            PressurekPa p = new PressurekPa(kpa);
            VolumeLitres v = new VolumeLitres(liters);
            TemperatureKelvin t = new TemperatureKelvin(kelvin);
            MoleQuantity q = IdealGas.Quantity(p, v, t);
            SpecificHeat c = Mole.GetSpecificHeat(gasType);
            MoleEnergy e = new MoleEnergy(t, c, q);
            Mole mole = new Mole(gasType, q, e);
            atmosphere.Add(mole);
        }
    }

    public static void RemoveGas(Atmosphere atmosphere, Chemistry.GasType gasType, float moles) {
        if (atmosphere != null && moles > 0f) {
            MoleQuantity q = new MoleQuantity(moles);
            atmosphere.Remove(q, gasType);
        }
    }

    public static GasMixture RemoveGasAllFromMixture(ref GasMixture mixture) {
        mixture.Reset();
        return mixture;
    }

    public static void RemoveGasAll(Atmosphere atmosphere) {
        if (atmosphere == null) {
            return;
        }
        switch (atmosphere.Mode) {
        case AtmosphereHelper.AtmosphereMode.World:
        case AtmosphereHelper.AtmosphereMode.Global:
            Room room = GridController.World.RoomController.GetRoom(atmosphere.Grid);
            if (room == null) {
                // World
                if (atmosphere.ReferenceId != 0) {
                    RemoveGasAllFromMixture(ref atmosphere.GasMixture); // Open World Room
                }
            } else {
                // Room
                foreach (WorldGrid grid in room.Grids) {
                    Atmosphere atmos = AtmosphericsManager.Find(grid);
                    if (atmos != null) {
                        RemoveGasAllFromMixture(ref atmos.GasMixture);
                    }
                }
            }
            break;
        case AtmosphereHelper.AtmosphereMode.None:
            break;
        case AtmosphereHelper.AtmosphereMode.Thing:
        default:
            // Thing, Pipe, etc.
            RemoveGasAllFromMixture(ref atmosphere.GasMixture);
            break;
        }
    }

    public static string DebugAtmosphere(Atmosphere atmosphere) {
        if (atmosphere == null) { return "null"; }
        // this will cache
        VolumeLitres v = atmosphere.GetGasVolume();
        TemperatureKelvin t = atmosphere.GasMixture.Temperature;
        PressurekPa p = IdealGas.Pressure(atmosphere.GasMixture.GetTotalMolesGasses, t, v);
        return $"{v.ToDouble():F2}L {p.ToDouble():F2}kPa {t.ToDouble():F2}K";
    }
}

public class SspDeviceConfig {
    private static SspDeviceConfig _instance;
    public static SspDeviceConfig Instance { get => _instance ??= new SspDeviceConfig(); }
    protected static Func<string, string> T = Translation.T;

    public Thing thing { get; private set; }
    public Device device { get; private set; } // pointer to thing if thing is Device
    public ConfigText text { get; private set; }

    public class ConfigText {
        public string name;
        public string prefabHash;
        public string prefabName;
        public string referenceId;
        public string logic;

        public static readonly LogicType[] HighPriorityLogicType = { LogicType.On, LogicType.Mode, LogicType.Setting, LogicType.Open, LogicType.Error };
        public static readonly LogicType[] MidPriorityLogicType = { LogicType.RequiredPower, LogicType.PowerGeneration, LogicType.Ratio, LogicType.Pressure, LogicType.Temperature, LogicType.TotalMoles };
        public static readonly LogicType[] ExcludeLogicType = { LogicType.NameHash, LogicType.PrefabHash, LogicType.ReferenceId }; // concat above arrays

        static ConfigText() {
            int len = ExcludeLogicType.Length;
            Array.Resize(ref ExcludeLogicType, len + HighPriorityLogicType.Length + MidPriorityLogicType.Length);
            Array.Copy(HighPriorityLogicType, 0, ExcludeLogicType, len, HighPriorityLogicType.Length);
            Array.Copy(MidPriorityLogicType, 0, ExcludeLogicType, len + HighPriorityLogicType.Length, MidPriorityLogicType.Length);
        }

        // don't clear name
        public void Clear() {
            prefabHash = prefabName = referenceId = logic = "";
        }
    }

    public SspDeviceConfig() {
        device = null;
        text = new ConfigText();
    }

    /// @ref ConfigCartridge.ScannedDevice.get
    public static bool OnUpdate() {
        bool changed = false;
        Thing thing = CursorManager.CursorThing;
        if (thing != Instance.thing) {
            changed = true;
            _instance.thing = thing;
            _instance.device = thing as Device;
        }
        _instance.UpdateText(changed);
        return changed;
    }

    /// @ref ConfigCartridge.ReadLogicText
    private void UpdateText(bool changed) {
        try {
            if (changed) {
                text.name = thing != null ? thing.DisplayName : "-";
            }
            if (null == thing) {
                text.Clear();
                return;
            }
            text.referenceId = '$' + thing.ReferenceId.ToString("X");
            text.prefabHash = thing.PrefabHash.ToString();
            text.prefabName = thing.PrefabName;
            if (null == device) {
                text.logic = "-";
            } else {
                int maxLogicCnt = 10;
                text.logic = "";
                foreach (LogicType logicType in ConfigText.HighPriorityLogicType) {
                    if (device.CanLogicRead(logicType)) {
                        double value = Math.Round(device.GetLogicValue(logicType), 3, MidpointRounding.AwayFromZero);
                        text.logic += $"{logicType} {value} ";
                        maxLogicCnt--;
                    }
                }
                foreach (LogicType logicType in ConfigText.MidPriorityLogicType) {
                    if (maxLogicCnt <= 0) {
                        break;
                    }
                    if (device.CanLogicRead(logicType)) {
                        double value = Math.Round(device.GetLogicValue(logicType), 3, MidpointRounding.AwayFromZero);
                        text.logic += $"{logicType} {value} ";
                        maxLogicCnt--;
                    }
                }
                for (int i = 0; maxLogicCnt > 0 && text.logic.Length < 128 && i < EnumCollections.LogicTypes.Length; i++) {
                    LogicType logicType = EnumCollections.LogicTypes.Values[i];
                    if (ConfigText.ExcludeLogicType.Contains(logicType)) {
                        continue;
                    }
                    if (device.CanLogicRead(logicType)) {
                        double value = Math.Round(device.GetLogicValue(logicType), 3, MidpointRounding.AwayFromZero);
                        text.logic += $"{logicType} {value} ";
                        maxLogicCnt--;
                    }
                }
            }
        } catch (Exception ex) {
            Log.LogError($"SspDeviceConfig UpdateText {ex}");
        }
    }
}

public class SspTerminalCommand {
    // args = strings after "serverrun ssp terminal"
    public static void ParseArgs(string[] args) {
        if (args.Length == 0) {
            Log.LogError("SspTerminalCommand ParseArgs no args");
            return;
        }
        // Log.LogInfo(String.Join(", ", args));
        switch (args[0]) {
        case "atmos":
            if (args.Length < 3) {
                Log.LogError("SspTerminalCommand ParseArgs atoms too few arguments");
                return;
            }
            Atmosphere atmosphere = TryParseAtmosphere(args[2]); // atoms <action> <atoms> <params...>
            if (atmosphere == null) {
                return;
            }
            switch (args[1]) {
            case "add": {
                if (args.Length < 6) {
                    Log.LogError("SspTerminalCommand ParseArgs atoms add too few args");
                    return;
                }
                uint type;
                float moles;
                float temp;
                if (!uint.TryParse(args[3], out type) || !float.TryParse(args[4], out moles) || !float.TryParse(args[5], out temp)) {
                    Log.LogError($"SspTerminalCommand ParseArgs atoms add parse error");
                    return;
                }
                AddGasCommand(atmosphere, (Chemistry.GasType)type, moles, temp);
                break;
            }
            case "clear":
                ClearGasCommand(atmosphere);
                break;
            case "remove": {
                if (args.Length < 5) {
                    Log.LogError("SspTerminalCommand ParseArgs atoms remove too few args");
                    return;
                }
                uint type;
                float moles;
                if (!uint.TryParse(args[3], out type) || !float.TryParse(args[4], out moles)) {
                    Log.LogError($"SspTerminalCommand ParseArgs atoms remove parse error");
                    return;
                }
                RemoveGasCommand(atmosphere, (Chemistry.GasType)type, moles);
                break;
            }
            }
            break;
        case "weather":
            if (args.Length == 1) {
                // weather : toggle weather event
                Log.LogInfo("SspTerminalCommand weather toggle");
                SetWeatherCommand(!SspWeather.IsWeatherEventRunning);
            } else {
                // weather <on|off> : set weather event
                switch (args[1]) {
                case "on":
                    Log.LogInfo("SspTerminalCommand weather on");
                    SetWeatherCommand(true);
                    break;
                case "off":
                    Log.LogInfo("SspTerminalCommand weather off");
                    SetWeatherCommand(false);
                    break;
                default:
                    Log.LogInfo("SspTerminalCommand weather unknown, abort");
                    break;
                }
            }
            break;
        default:
            Log.LogError($"SspTerminalCommand ParseArgs unknown operation \"{args[1]}\"");
            break;
        }
    }

    public const string ServerRunCommand = "serverrun ssp terminal";

    public static void AddGasCommand(Atmosphere atmosphere, Chemistry.GasType gasType, float moles, float temperature) {
        if (NetworkManager.IsClient) {
            string command = $"{ServerRunCommand} atmos add {atmosphere.ReferenceId} {(uint)gasType} {moles} {temperature}";
            Log.LogInfo(command);
            CommandLine.Process(command);
        } else {
            // Log.LogInfo("AddGasCommand");
            SspAtmosphere.AddGas(atmosphere, gasType, moles, temperature);
        }
    }

    public static void ClearGasCommand(Atmosphere atmosphere) {
        if (NetworkManager.IsClient) {
            string command = $"{ServerRunCommand} atmos clear {atmosphere.ReferenceId}";
            Log.LogInfo(command);
            CommandLine.Process(command);
        } else {
            // Log.LogInfo("ClearGasCommand");
            SspAtmosphere.RemoveGasAll(atmosphere);
        }
    }

    public static void RemoveGasCommand(Atmosphere atmosphere, Chemistry.GasType gasType, float moles) {
        if (NetworkManager.IsClient) {
            string command = $"{ServerRunCommand} atmos remove {atmosphere.ReferenceId} {(uint)gasType} {moles}";
            Log.LogInfo(command);
            CommandLine.Process(command);
        } else {
            // Log.LogInfo("RemoveGasCommand");
            SspAtmosphere.RemoveGas(atmosphere, gasType, moles);
        }
    }

    public static void SetWeatherCommand(bool enable) {
        if (NetworkManager.IsClient) {
            string command = $"{ServerRunCommand} set weather ";
            command += enable ? "on" : "off";
            Log.LogInfo(command);
            CommandLine.Process(command);
        } else {
            SspWeather.ServerSetWeather(enable);
        }
    }


    private static Atmosphere TryParseAtmosphere(string refId) {
        long atmosphereId;
        if (!long.TryParse(refId, out atmosphereId)) {
            Log.LogError("SspTerminalCommand TryParseAtmosphere error");
            return null;
        }
        Atmosphere atmo = SspAtmosphere.GetAtmosphereByRefId(atmosphereId);
        if (atmo == null) {
            Log.LogError($"SspTerminalCommand TryParseAtmosphere {refId} not found in {AtmosphericsManager.AllAtmospheres.ActiveCount} atmos");
            return null;
        }
        string name = SspAtmosphere.GetAtmosphereName(atmo);
        Log.LogInfo($"SspTerminalCommand TryParseAtmosphere {refId} found \"{name}\"");
        return atmo;
    }
}
